This notebook will demonstrate how to incorporate composition-dependent diffusion in a kinetic multilayer model created by MultilayerPy.
In this example, we will use a simple reaction scheme where 1 + 2 --> 3 and the diffusion of 1 and 2 is affected by the amount of 3 in the system.
Lets call component 1: oleic acid; component 2: ozone and component 3: products.
The options for composition-dependent diffusion for component $i$ in component $j$ with their respective mole fractions ($\phi$) are below:
1) Vignes-type diffusion $$D_{i} = (D_{i,self})^{\phi_{i}} (D_{i,j})^{\phi_{j}}$$
2) Linear combination of diffusivities
$$D_{i} = \phi_{i}D_{i,self} + \phi_{j}D_{i,j}$$3) Obstruction theory
$$D_{i} = \frac{D_{i,self}(2 - 2\phi_{j})}{(2 + \phi_{j})} $$The model building process is much the same as in the crash course:
# import necessary packages
import numpy as np
import multilayerpy
import multilayerpy.build as build
import multilayerpy.simulate as simulate
import multilayerpy.optimize as optimize
# check the version
print(multilayerpy.__version__)
# import classes needed to build the model
from multilayerpy.build import ModelType, ReactionScheme, DiffusionRegime, ModelComponent
# define the model type (spherical geometry)
mod_type = ModelType('km-sub','spherical')
# build the reaction tuple list, in this case only 1 tuple in the list (for 1 reaction)
# component 1 (oleic acid) reacts with component 2 (ozone)
reaction_tuple_list = [(1,2)]
# build the product tuple list, only component 3 (products) is a product
# a tuple with a single value inside is defined (value,)
product_tuple_list = [(3,)]
# now construct the reaction scheme
# we can give it a name and define the number of components as below
reaction_scheme = ReactionScheme(mod_type,name='Oleic acid ozonolysis',
reactants=reaction_tuple_list,
products=product_tuple_list)
# let's print out a representation of the reaction scheme
reaction_scheme.display()
# Adding in model components
# oleic acid
OA = ModelComponent(1,reaction_scheme,name='Oleic acid')
# ozone, declare that it is a gas
O3 = ModelComponent(2,reaction_scheme,gas=True,name='Ozone')
# products
prod = ModelComponent(3,reaction_scheme, name='Reaction products')
# collect into a dictionary
model_components_dict = {'1':OA,
'2':O3,
'3':prod}
Now we want the diffusion of components 1 and 2 (oleic acid and ozone) to be dependent on the amount of component 3 (some viscous product).
To do this, we provide a dictionary where the keys represent each component number and the values are tuples which contain the component number(s) of the other reaction component(s) which affect the diffusion of the component in question.
The default diffusion regime is a Vignes-type regime.
# making the diffusion dictionary
# diffusion of component 1 and 2 dependent on the amount of 3
# diffusion of component 3 not composition dependent
diff_dict = {'1' : (3,),
'2': (3,),
'3':None}
# make diffusion regime
diff_regime = DiffusionRegime(mod_type,model_components_dict,diff_dict=diff_dict, regime='vignes')
# linear combination of diffusivities (uncomment to use)
#diff_regime = DiffusionRegime(mod_type,model_components_dict,diff_dict=diff_dict, regime='linear')
# obstruction theory (uncomment to use)
#diff_regime = DiffusionRegime(mod_type,model_components_dict,diff_dict=diff_dict, regime='obstruction')
# call it to build diffusion code ready for the builder (REMEMBER THIS)
diff_regime()
# Now construct the model and identify the required parameters
# import ModelBuilder class
from multilayerpy.build import ModelBuilder
# create the model object, ignore [1,2,3] etc at the end
model = ModelBuilder(reaction_scheme,model_components_dict,diff_regime)
# build the model. Will save a file, don't include the date in the model filename
model.build(date=False)
# print out the parameters required for the model to run
print(model.req_params)
Notice that Db_1_3 and Db_2_3 are included in the list of required parameters. Db_1_3 stands for "diffusion of component 1 in pure component 3". These parameters would not be required if composition-dependent diffusion was not imposed.
These parameters need to be supplied when running the model. Product 3 has been made to be viscous (1e-14 cm2 s-1).
# Build and run the simulation
# import the Simulate class
from multilayerpy.simulate import Simulate
# import the Parameter class
from multilayerpy.build import Parameter
# make the parameter dictionary
param_dict = {'delta_3':Parameter(1e-7),
'alpha_s_0_2':Parameter(4.2e-4),
'delta_2':Parameter(0.4e-7),
'Db_2':Parameter(1e-5),
'delta_1':Parameter(0.8e-7),
'Db_1':Parameter(1e-10),
'Db_3':Parameter(1e-18),
'k_1_2':Parameter(1.7e-15),
'H_2':Parameter(4.8e-4),
'Xgs_2': Parameter(7.0e13),
'Td_2': Parameter(1e-2),
'w_2':Parameter(3.6e4),
'T':Parameter(298.0),
'k_1_2_surf':Parameter(6.0e-12),
'Db_1_3':Parameter(1e-12),
'Db_2_3':Parameter(1e-8)}
# make the simulate object with the model and parameter dictionary
sim = Simulate(model,param_dict)
# define required parameters
n_layers = 100
rp = 0.2e-4 # radius in cm
time_span = [0,40] # in s
n_time = 999 # number of timepoints to save to output
#spherical V and A
# use simulate.make_layers function
V, A, layer_thick = simulate.make_layers(mod_type,n_layers,rp)
# initial conc. of everything
bulk_conc_dict = {'1':1.21e21,'2':0,'3':0} # key=model component number, value=bulk conc
surf_conc_dict = {'1':9.68e13,'2':0,'3':0} # key=model component number, value=surf conc
y0 = simulate.initial_concentrations(mod_type,bulk_conc_dict,surf_conc_dict,n_layers)
# now run the model (this may take a few moments)
output = sim.run(n_layers,rp,time_span,n_time,V,A,layer_thick,Y0=y0)
%matplotlib inline
# plot the model
fig = sim.plot()
# plot the bulk concentrations
sim.plot_bulk_concs()
As you can see, ozone diffusion is inhibited by the viscous reaction products acting as a diffusional barrier. This results in the interesting decay trace.
The next block of code will compare with and without this diffusion regime.
(Ignore the warning about supplying unnecessary parameters to the model below)
# make a an empty list for simulate objects, append to later
sim_objs = []
# non-composition-dependent diffusion
non_comp_diff_dict = None
# composition-dependent diffusion
comp_diff_dict = {'1' : (3,),
'2': (3,),
'3':None}
diff_dicts = [non_comp_diff_dict,comp_diff_dict]
mod_name_ext = ['without','with']
for index, d_dict in enumerate(diff_dicts):
model_comps_dict = {'1':OA,
'2':O3,
'3':prod}
# make diff regime
diff_regime = DiffusionRegime(mod_type,model_comps_dict,diff_dict=d_dict)
# call diff regime
diff_regime()
# create the model object
model = ModelBuilder(reaction_scheme,model_components_dict,diff_regime)
model.build(date=False,name_extention=mod_name_ext[index])
# make simulate object and run it, use same params as before
# The extra Db_1_3 and Db_2_3 params will be ignored by the non-composition-dependent diffusion simulation
# make the simulate object with the model and parameter dictionary
sim = Simulate(model,param_dict)
# define required parameters
n_layers = 100
rp = 0.2e-4 # radius in cm
time_span = [0,40] # in s
n_time = 999 # number of timepoints to save to output
#spherical V and A
# use simulate.make_layers function
V, A, layer_thick = simulate.make_layers(mod_type,n_layers,rp)
# initial conc. of everything
bulk_conc_dict = {'1':1.21e21,'2':0,'3':0} # key=model component number, value=bulk conc
surf_conc_dict = {'1':9.68e13,'2':0,'3':0} # key=model component number, value=surf conc
y0 = simulate.initial_concentrations(mod_type,bulk_conc_dict,surf_conc_dict,n_layers)
# now run the model
output = sim.run(n_layers,rp,time_span,n_time,V,A,layer_thick,Y0=y0)
# append the simulate object to the list
sim_objs.append(sim)
# Plotting without composition-dependent diffusion
sim = sim_objs[0]
fig = sim.plot()
# plotting with composition-dependent diffusion
sim = sim_objs[1]
fig = sim.plot()
This tutorial demonstrated the use of composition-dependent diffusion in MultilayerPy. When considering potentially viscous systems, the composition of the particle or film can have a significant impact on reaction kinetics. This is an advantage of kinetic multi-layer modelling.